use std::{rc::Rc, cell::RefCell};

use crate::{instructions::base::Instruction, rtda::Frame};

// 复制栈顶变量
// Duplicate the top operand stack value
#[derive(Debug, Default)]
pub struct DUP {}

// bottom -> top
// [...][c][b][a]
//              \_
//                |
//                V
// [...][c][b][a][a]
impl Instruction for DUP {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let stack = frame.borrow().get_operand_stack();
        let mut stack = stack.borrow_mut();
        let slot = stack.pop_slot();
        stack.push_slot(slot.clone());
        stack.push_slot(slot);
    }
}

// Duplicate the top operand stack value and insert two values down
#[derive(Debug, Default)]
pub struct DUP_X1 {}

// bottom -> top
// [...][c][b][a]
//           __/
//          |
//          V
// [...][c][a][b][a]
impl Instruction for DUP_X1 {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let stack = frame.borrow().get_operand_stack();
        let mut stack = stack.borrow_mut();
        let slot1 = stack.pop_slot();
        let slot2 = stack.pop_slot();
        stack.push_slot(slot1.clone());
        stack.push_slot(slot2);
        stack.push_slot(slot1);
    }
}


// Duplicate the top operand stack value and insert two or three values down
#[derive(Debug, Default)]
pub struct DUP_X2 {}

// bottom -> top
// [...][c][b][a]
//        _____/
//       |
//       V
// [...][a][c][b][a]
impl Instruction for DUP_X2 {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let stack = frame.borrow().get_operand_stack();
        let mut stack = stack.borrow_mut();
        let slot1 = stack.pop_slot();
        let slot2 = stack.pop_slot();
        let slot3 = stack.pop_slot();
        stack.push_slot(slot1.clone());
        stack.push_slot(slot3);
        stack.push_slot(slot2);
        stack.push_slot(slot1);
    }
}


// Duplicate the top operand stack value and insert two values down
#[derive(Debug, Default)]
pub struct DUP2 {}

// bottom -> top
// [...][c][b][a]____
//           \____   |
//                |  |
//                V  V
// [...][c][b][a][b][a]
impl Instruction for DUP2 {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let stack = frame.borrow().get_operand_stack();
        let mut stack = stack.borrow_mut();
        let slot1 = stack.pop_slot();
        let slot2 = stack.pop_slot();
        stack.push_slot(slot2.clone());
        stack.push_slot(slot1.clone());
        stack.push_slot(slot2);
        stack.push_slot(slot1);
    }
}


// Duplicate the top one or two operand stack values and insert two or three values down
#[derive(Debug, Default)]
pub struct DUP2_X1 {}

// bottom -> top
// [...][c][b][a]
//        _/ __/
//       |  |
//       V  V
// [...][b][a][c][b][a]
impl Instruction for DUP2_X1 {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let stack = frame.borrow().get_operand_stack();
        let mut stack = stack.borrow_mut();
        let slot1 = stack.pop_slot();
        let slot2 = stack.pop_slot();
        let slot3 = stack.pop_slot();
        stack.push_slot(slot2.clone());
        stack.push_slot(slot1.clone());
        stack.push_slot(slot3);
        stack.push_slot(slot2);
        stack.push_slot(slot1);
    }
}


// Duplicate the top one or two operand stack values and insert two, three, or four values down
#[derive(Debug, Default)]
pub struct DUP2_X2 {}

// bottom -> top
// [...][d][c][b][a]
//        ____/ __/
//       |   __/
//       V  V
// [...][b][a][d][c][b][a]
impl Instruction for DUP2_X2 {
    fn execute(&mut self, frame: Rc<RefCell<Frame>>) {
        let stack = frame.borrow().get_operand_stack();
        let mut stack = stack.borrow_mut();
        let slot1 = stack.pop_slot();
        let slot2 = stack.pop_slot();
        let slot3 = stack.pop_slot();
        let slot4 = stack.pop_slot();
        stack.push_slot(slot2.clone());
        stack.push_slot(slot1.clone());
        stack.push_slot(slot4);
        stack.push_slot(slot3);
        stack.push_slot(slot2);
        stack.push_slot(slot1);
    }
}
